/*
 * Tester.c
 *
 *  Created on: 29/04/2012
 *      Author: ElaD
 */

#include <hw2_syscalls.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/resource.h>
#include <errno.h>

#define ASSERT_PRINT( b, str ) if( (b) ){ perror(str); exit(-1); }
#define ASSERT_ERRNO_PRINT( b, str ) if( errno != (b) ){ printf(str); exit(-1); }
int fibonaci(int n){
	if (n < 2)
		return n;
	return fibonaci(n-1) + fibonaci(n-2);
}

int checkRandParam(int param){
	return (param >100 || param < 1);
}

char* printBool(int flag){
	if(flag){
		return "yes";
	}
	return "no";
}

char* printReason(int reason){
	switch(reason){
		case 1: return "Task Created.";
		break;
		case 2: return "Task Ended.";
		break;
		case 3: return "Task yielded the CPU.";
		break;
		case 4: return "Task goes out for waiting.";
		break;
		case 5: return "Higher priority task returns from waiting.";
		break;
		case 6: return "Task's timeslice ended.";
		break;
		default: return "Bad reason number.";
	}
}


char* printMech(int mode){
	switch(mode){
		case MODE_RANDOM: return "Mode Random.";
		break;
		case MODE_REGULAR: return "Mode Regular.";
		break;
		default: return "Bad mode.";
	}
}
void printStatistics(struct switch_info* info, int size){
	printf("Scheduling statistics:\n");
	printf("\t\t Mechanism\tP_pid \t N_pid \t P_prio\tN_prio\tB_prio\tN_Epoch\tTime\t\tReason\n");
	for(int i=0; i<size; i++){
		printf("\t %d:\t %s \t %d \t %d \t %d \t %d \t %d \t %s \t %lu \t %s \n",			\
				i+1, printMech(info[i].cur_mechanism), info[i].previous_pid, info[i].next_pid, info[i].previous_priority, info[i].next_priority, info[i].best_priority, \
				printBool(info[i].is_new_epoch), info[i].time, printReason(info[i].reason));
	}
}

int main(int argc, const char** argv){
	if(argc<6){
		printf("Usage: sched_tester <p_prio><p_epoch><p_interactive> <nice1> <n1> ...\n");
		return 0;
	}

	int res, status, i;
	struct random_params rand_params;
	struct switch_info cur_info[150];
	if( checkRandParam(atoi(argv[1])) || checkRandParam(atoi(argv[2])) || checkRandParam(atoi(argv[3])) ){
		printf("Wrong Parameters!\n");
		return 0;
	}
	rand_params.p_prio = atoi(argv[1]);
	rand_params.p_epoch = atoi(argv[2]);
	rand_params.p_interactive = atoi(argv[3]);

	//Testing Bad Input
	res = sched_setmechanism(MODE_REGULAR, &rand_params);
	ASSERT_ERRNO_PRINT( EINVAL, "91: set_mechnism should report bad input");
	res = sched_setmechanism(MODE_RANDOM, &rand_params);
	ASSERT_ERRNO_PRINT( EINVAL, "93: set_mechnism should report bad input");
	res = is_interactive(-1);
	ASSERT_ERRNO_PRINT( ESRCH, "95: is_interactive should report no task found");
	int curr_mech = sched_getmechanism(&rand_params);
	ASSERT_PRINT( curr_mech == -1, "97: sched_getmechanism random");
	//Testing for real
	res = sched_setmechanism(MODE_RANDOM, &rand_params);
	ASSERT_PRINT( res == -1, "100: sched_setmechanism random");
	if(curr_mech != res){
		perror("sched_setmechanism - wrong return value");
		exit(-1);
	}
	res = sched_getmechanism(&rand_params);
	if (res == -1){
		perror("sched_getmechanism random");
		exit(-1);
	}
	if (res != MODE_RANDOM){
		perror("wrong return value in sched_getmechanism");
		exit(-1);
	}

	for(i=4; i<argc; i+=2){
		int f = fork();
		if(f<0){
			perror("Fork failed!");
			exit(-1);
		}
		if(!f){
			if(atoi(argv[i]) >19 || atoi(argv[i]) <-20 || atoi(argv[i+1]) <0){
				printf("Bad parameters\n");
				return -1;
			}
			setpriority(PRIO_PROCESS, 0, atoi(argv[i]));
			printf("Fork number %d is %sInteractive.\n",(i-2)/2, is_interactive(f) ? "" : "not ");
			return fibonaci(atoi(argv[i+1]));
		}
	}
	while(wait(&status) != -1);
	res = get_scheduling_statistic(cur_info);
	if (res == -1) {
		perror("get_scheduling_statistic");
		exit(-1);
	}
	printStatistics(cur_info, res);

	res = sched_setmechanism(MODE_REGULAR, NULL);
	if (res == -1){
		perror("sched_setmechanism regular");
		exit(-1);
	}

}
